﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using GraphAPI.Model;

namespace GraphAPI
{
	public delegate void DeleteVertexHandler(Vertex v);

	public partial class Vertex : Control
	{
		private Color objectStrokeColor = Color.Black;
        private GraphPanel graphPanel1;

		[Category("Appearance")]
		public Color StrokeColor
		{
			get { return objectStrokeColor; }
			set
			{
				objectStrokeColor = value;
				InvalidateEx();
			}
		}

		private Entity entity;

		public Entity Entity
		{
			get { return entity; }
		}

		protected override void OnTextChanged(EventArgs e)
		{
			entity.Name = this.Text;
			base.OnTextChanged(e);

			AdjustSize();
		}

		public double Weight
		{
			get { return entity.Weight; }
			set
			{
				entity.Weight = value;
				AdjustSize();
			}
		}

		public string FullCaption
		{
			get
			{
				if (entity.Weight.Equals(double.NaN))
					return Text;
				else
					return Text + " (" + entity.Weight.ToString() + ")";
			}
		}

		private SizeF GetCaptionSize(Graphics g)
		{
			return g.MeasureString(this.FullCaption, this.Font);	
		}

		public Vertex(Entity entity)
		{
			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			SetStyle(ControlStyles.ResizeRedraw, true);

			//Set style for double buffering
			SetStyle(ControlStyles.AllPaintingInWmPaint |
					 ControlStyles.UserPaint, true);

			SetStyle(ControlStyles.Opaque, true);

//			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

			this.entity = entity;

			this.NodeSize = entity.Radius;

			this.BackColor = Color.FromArgb(0, Color.Red);

			this.Center = entity.Position;

			this.ForeColor = Color.White;

			this.TextAlign = entity.TextAlign;

			this.Text = entity.Name;

			if (entity.ColorMark == Color.Empty)
				this.ForeColor = Color.White;
			else
				this.ForeColor = entity.ColorMark;

		}

		public event DeleteVertexHandler DeleteVertex;

		public void Delete()
		{
			if (DeleteVertex != null)
				DeleteVertex(this);
		}

		private int nodeSize;

		[Category("Appearance")]
		public int NodeSize
		{
			get { return nodeSize; }
			set
			{
				nodeSize = value;
				entity.Radius = nodeSize;
				AdjustSize();
			}
		}

		public string Commentary
		{
			get { return entity.Commentary; }
			set { entity.Commentary = value; }
		}

		private Color textColor = Color.Black;

		[Category("Appearance")]
		public Color TextColor
		{
			get { return textColor; }
			set
			{
				textColor = value;
				InvalidateEx();
			}
		}

		private Point center;

		public Point Center
		{
			get { return center; }
			set
			{
				Point newLocation = new Point(Left + value.X - center.X,
											Top + value.Y - center.Y);
				this.Location = newLocation;
			}
		}

		bool locked = false;

		private void AdjustSize()
		{
			if (locked)
				return;

			locked = true;

			Graphics g = CreateGraphics();
			SizeF size = GetCaptionSize(g);	

			int width, height;
			switch (textAlign)
			{
				case TextAlign.Top:
					width = Math.Max((int)size.Width, nodeSize * 2);
					height = (int)size.Height + nodeSize * 2;
					this.SetBounds(center.X - width / 2, center.Y - nodeSize - (int)size.Height, width, height);
					break;

				case TextAlign.Bottom:
					width = Math.Max((int)size.Width, nodeSize * 2);
					height = (int)size.Height + nodeSize * 2;
					this.SetBounds(center.X - width / 2, center.Y - nodeSize, width, height);
					break;

				case TextAlign.Right:
					width = (int)size.Width + nodeSize * 2;
					height = Math.Max((int)size.Height, nodeSize * 2);
					this.SetBounds(center.X - nodeSize, center.Y - height / 2, width, height);
					break;

				case TextAlign.Left:
					width = (int)size.Width + nodeSize * 2;
					height = Math.Max((int)size.Height, nodeSize * 2);
					this.SetBounds(center.X - nodeSize - (int)size.Width, center.Y - height / 2, width, height);
					break;

				case TextAlign.Center:
					width = Math.Max((int)size.Width, nodeSize * 2);
					height = Math.Max((int)size.Height, nodeSize * 2);
					this.SetBounds(center.X - width / 2, center.Y - height / 2, width, height);
					break;

				case TextAlign.None:
					this.SetBounds(center.X - nodeSize, center.Y - nodeSize, nodeSize * 2, nodeSize * 2);
					break;
			}

			Rectangle ellipseRect = new Rectangle(this.center.X - this.Left - nodeSize - 1,
								this.center.Y - this.Top - nodeSize - 1,
								nodeSize * 2 + 1,
								nodeSize * 2 + 1);

			GraphicsPath path = new GraphicsPath();
			path.AddEllipse(ellipseRect);
			Region r = new Region(path);

			if (textAlign != TextAlign.None)
			{
				PointF captionPoint = GetLabelPosition(g);
				r.Union(new RectangleF(captionPoint, size));
			}

			this.Region = r;

			g.Dispose();

			locked = false;
		}

		private TextAlign textAlign = TextAlign.Center;

		[Category("Appearance")]
		public TextAlign TextAlign
		{
			get { return textAlign; }
			set
			{
				textAlign = value;
				entity.TextAlign = textAlign;
				AdjustSize();
			}
		}

		protected override CreateParams CreateParams
		{
			get
			{
				CreateParams cp = base.CreateParams;
				cp.ExStyle |= 0x20;
				return cp;
			}
		}

		private void InvalidateEx()
		{
			if (Parent == null)
				return;

			Rectangle rc = new Rectangle(this.Location, this.Size);
			Parent.Invalidate(rc, true);
		}

		protected override void  OnFontChanged(EventArgs e)
		{
			entity.ColorMark = this.ForeColor;
 			base.OnFontChanged(e);
		}

		private PointF GetLabelPosition(Graphics g)
		{
			PointF captionPoint = new PointF(0, 0);
			SizeF captionSize = GetCaptionSize(g);
			switch (textAlign)
			{
				case TextAlign.Center:
					captionPoint = new PointF((this.Width - captionSize.Width) / 2, (this.Height - captionSize.Height) / 2);
					break;

				case TextAlign.Top:
					captionPoint = new PointF((this.Width - captionSize.Width) / 2, 0);
					break;

				case TextAlign.Bottom:
					captionPoint = new PointF((this.Width - captionSize.Width) / 2, this.Height - captionSize.Height);
					break;

				case TextAlign.Left:
					captionPoint = new PointF(0, (this.Height - captionSize.Height) / 2);
					break;

				case TextAlign.Right:
					captionPoint = new PointF(this.Width - captionSize.Width, (this.Height - captionSize.Height) / 2);
					break;
			}

			return captionPoint;
		}

		protected override void OnPaint(PaintEventArgs e)
		{
			e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

			Rectangle ellipseRect = new Rectangle(this.center.X - this.Left - nodeSize,
								this.center.Y - this.Top - nodeSize,
								nodeSize * 2 - 1,
								nodeSize * 2 - 1);

			e.Graphics.FillEllipse(new SolidBrush(ForeColor), ellipseRect);
			e.Graphics.DrawEllipse(new Pen(objectStrokeColor), ellipseRect);

			//Point middle = new Point(center.X - this.Left, center.Y - this.Top);
			//e.Graphics.DrawEllipse(Pens.Brown, middle.X, middle.Y, 1, 1);

			if (textAlign != TextAlign.None)
			{
				PointF captionPoint = GetLabelPosition(e.Graphics);
				e.Graphics.DrawString(this.FullCaption, this.Font, new SolidBrush(this.TextColor), captionPoint);
			}
			
			e.Graphics.Dispose();
			base.OnPaint(e);
		}

		protected override void OnResize(EventArgs e)
		{
			if (locked)
				return;

			int width = nodeSize * 2, height = nodeSize * 2;
			Graphics g = CreateGraphics();
			SizeF size = GetCaptionSize(g);
			g.Dispose();
			switch (textAlign)
			{
				case TextAlign.Top:
				case TextAlign.Bottom:
					width = this.Width;
					height = this.Height - (int)size.Height;
					break;

				case TextAlign.Right:
				case TextAlign.Left:
					width = this.Width -(int)size.Width;
					height = this.Height;
					break;

				case TextAlign.Center:
					width = Width;
					height = Height;
					break;

				case TextAlign.None:
					width = Width;
					height = Height;
					break;
			}

			int diametr = Math.Min(width, height);
			nodeSize = diametr / 2;
			entity.Radius = nodeSize;

			AdjustSize();

			base.OnResize(e);
		}

		private Point GetCenter()
		{
			int left = this.Left + this.Width / 2, top = this.Top + this.Height / 2;
			Graphics g = CreateGraphics();
			SizeF size = GetCaptionSize(g);
			switch (textAlign)
			{
				case TextAlign.Top:
					left = this.Left + this.Width / 2;
					top = this.Top + nodeSize + (int)size.Height;
					break;

				case TextAlign.Bottom:
					left = this.Left + this.Width / 2;
					top = this.Top + nodeSize;
					break;

				case TextAlign.Right:
					left = this.Left + nodeSize;
					top = this.Top + this.Height / 2;
					break;

				case TextAlign.Left:
					left = this.Left + nodeSize + (int)size.Width;
					top = this.Top + this.Height / 2;
					break;

				case TextAlign.Center:
					left = this.Left + this.Width / 2;
					top = this.Top + this.Height / 2;
					break;
			}

			return new Point(left, top);
		}

		protected override void OnMove(EventArgs e)
		{
			if (locked)
				return;

			center = GetCenter();
			entity.Position  = center;
			InvalidateEx();
			base.OnMove(e);
		}

        private void InitializeComponent()
        {
            GraphAPI.Model.Graph graph1 = new GraphAPI.Model.Graph();
            this.graphPanel1 = new GraphAPI.GraphPanel();
            this.SuspendLayout();
            // 
            // graphPanel1
            // 
            this.graphPanel1.Graph = graph1;
            this.graphPanel1.Location = new System.Drawing.Point(0, 0);
            this.graphPanel1.Name = "graphPanel1";
            this.graphPanel1.ReadOnly = false;
            this.graphPanel1.Size = new System.Drawing.Size(231, 201);
            this.graphPanel1.State = GraphAPI.GraphPanel.CursorState.Pointer;
            this.graphPanel1.TabIndex = 0;
            this.ResumeLayout(false);

        }
	}
}
